﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using System.Windows.Media.Imaging;

namespace StandaloneSilverlightWoZ
{
    public static class SilverlightWoZApplication
    {
        const string XAPNAME = "/StandaloneSilverlightWoZ.xap";
        const string CLIENTBINFOLDER = "/ClientBin";
        const string WEBSERVERRECIEVER = "CollectedDataFileReciever.ashx";
        const string COLLECTEDDATAFILEEXTENSION = ".sldx";

        /// <summary>
        /// Map from page number to page file location
        /// </summary>
        public static Dictionary<int, String> PageLookupTable = null;
        public static ActiveStoryPage CurrentPage = null;
        private static Canvas _mainCanvas = null;
        private static Page _silverlightHostPage = null;
        public static String ProjectName = null;
        public static int StartupPageNumber = -1;
        public static Queue<CollectedDataItem> userPageSequencedData = null;
        /// <summary>
        /// Relative path to the project file.
        /// </summary>
        public static String ProjectFileLocation;

        public static void SetupWizardOfOz(Page silverlightHostPage, Canvas canvas)
        {
            _mainCanvas = canvas;
            _silverlightHostPage = silverlightHostPage;
            userPageSequencedData = new Queue<CollectedDataItem>();
            string uri = GetAbsoluteDataPath("ActiveStorySettings.slsx");
            // Begin the download.
            WebClient settingsWebClient = new WebClient();
            settingsWebClient.OpenReadCompleted += new OpenReadCompletedEventHandler(settingsWebClient_OpenReadCompleted);
            settingsWebClient.OpenReadAsync(new Uri(uri));
        }
        public static void LoadProject()
        {
            string uri = GetAbsoluteDataPath(ProjectFileLocation);
            // Begin the download.
            WebClient loadProjectWebClient = new WebClient();
            loadProjectWebClient.OpenReadCompleted += new OpenReadCompletedEventHandler(loadProjectWebClient_OpenReadCompleted);
            loadProjectWebClient.OpenReadAsync(new Uri(uri));
        }
        public static void LoadPage(int pageNumber)
        {
            if (!PageLookupTable.ContainsKey(pageNumber))
                return;
            string uri = GetAbsoluteDataPath(PageLookupTable[pageNumber]);
            // Begin the download.
            WebClient loadPageWebClient = new WebClient();
            loadPageWebClient.OpenReadCompleted += new OpenReadCompletedEventHandler(loadPageWebClient_OpenReadCompleted);
            loadPageWebClient.OpenReadAsync(new Uri(uri));
        }
        public static void UploadData()
        {
            userPageSequencedData.Enqueue(new CollectedDataItem(CurrentPage.PageNumber, _silverlightHostPage.AccumulatedTime, _silverlightHostPage.MousePointList.ToArray(), _silverlightHostPage.CommentsTextBox.Text, -10));
            if (userPageSequencedData.Count == 0)
            {
                return;
            }
            string uri = GetAbsoluteServerPath(WEBSERVERRECIEVER);
            WebClient uploadDataWebClient = new WebClient();
            uploadDataWebClient.OpenWriteCompleted += new OpenWriteCompletedEventHandler(uploadDataWebClient_OpenWriteCompleted);
            UriBuilder ub = new UriBuilder(uri);
            ub.Query = string.Format("filename={0}", CreateUserDataFilename());
            uploadDataWebClient.OpenWriteAsync(ub.Uri);
        }

        private static string CreateUserDataFilename()
        {
            return Guid.NewGuid().ToString("N") + COLLECTEDDATAFILEEXTENSION;
        }


        static void PageTransitioning(int sourceButtonNumber)
        {
            _silverlightHostPage.PauseButton_Clicked(null, null);
            userPageSequencedData.Enqueue(new CollectedDataItem(CurrentPage.PageNumber, _silverlightHostPage.AccumulatedTime, _silverlightHostPage.MousePointList.ToArray(), _silverlightHostPage.CommentsTextBox.Text, sourceButtonNumber));
            _silverlightHostPage.CommentsTextBox.Text = String.Empty;
            _silverlightHostPage.AccumulatedTime = new TimeSpan(0);
            _silverlightHostPage.MousePointList.Clear();
            _silverlightHostPage.ResumeButton_Clicked(null, null);
        }

        static void settingsWebClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                throw new Exception("Could not open stream.");
                // TODO: (Add code to display error or downgrade gracefully.)
            }
            else
            {
                Stream stream = e.Result;
                XmlReaderSettings xrs = new XmlReaderSettings();
                xrs.CloseInput = true;
                XmlReader xr = XmlReader.Create(stream, xrs);
                ParseSettingsFile(xr);
                xr.Close();
                
                LoadProject();

            }
        }

        static void ParseSettingsFile(XmlReader xr)
        {
            xr.ReadStartElement(SilverlightSettingsXmlNames.RootElement);

            if (xr.NodeType == XmlNodeType.None)
            {
                xr.Close();
                return;
            }
            xr.ReadStartElement(SilverlightSettingsXmlNames.ProjectFileLocationElement);
            ProjectFileLocation = xr.ReadContentAsString();
            xr.ReadEndElement();

            xr.ReadStartElement(SilverlightSettingsXmlNames.TaskInstructionsElement);
            xr.ReadContentAsString();
            xr.ReadEndElement();

            xr.ReadEndElement();
        }

        static void loadProjectWebClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                throw new Exception("Could not open stream.");
                // TODO: (Add code to display error or downgrade gracefully.)
            }
            else
            {
                Stream stream = e.Result;
                XmlReaderSettings xrs = new XmlReaderSettings();
                xrs.CloseInput = true;
                XmlReader xr = XmlReader.Create(stream, xrs);
                ParseProjectFile(xr);
                xr.Close();

                LoadPage(StartupPageNumber);
            }
        }

        private static void ParseProjectFile(XmlReader xr)
        {
            xr.ReadStartElement(ProjectSilverlightXmlNames.RootElement);
            
            xr.ReadStartElement(ProjectSilverlightXmlNames.ProjectNameElement);
            ProjectName = xr.ReadContentAsString();
            xr.ReadEndElement();

            xr.ReadStartElement(ProjectSilverlightXmlNames.StartupPageElement);
            StartupPageNumber = xr.ReadContentAsInt();
            xr.ReadEndElement();

            xr.ReadStartElement(ProjectSilverlightXmlNames.PageCollectionElement);

            xr.ReadStartElement(ProjectSilverlightXmlNames.PageCountElement);
            int pageCount = xr.ReadContentAsInt();
            xr.ReadEndElement();

            PageLookupTable = new Dictionary<int, String>();
            for (int i = 0; i < pageCount; i++)
            {
                xr.ReadStartElement(ProjectSilverlightXmlNames.PageFileElement);

                xr.ReadStartElement(ProjectSilverlightXmlNames.PageNumberElement);
                int pageNumber = xr.ReadContentAsInt();
                xr.ReadEndElement();

                xr.ReadStartElement(ProjectSilverlightXmlNames.PageFileLocationElement);
                string pageFileLocationElement = xr.ReadContentAsString();
                xr.ReadEndElement();

                xr.ReadEndElement();
                PageLookupTable.Add(pageNumber, pageFileLocationElement);
            }


            xr.ReadEndElement();   //End Page Collection Element

            xr.ReadEndElement();   //End Root Element


        }

        static void loadPageWebClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                throw new Exception("Could not open stream.");
                // TODO: (Add code to display error or downgrade gracefully.)
            }
            else
            {
                Stream stream = e.Result;
                XmlReaderSettings xrs = new XmlReaderSettings();
                xrs.CloseInput = true;
                XmlReader xr = XmlReader.Create(stream, xrs);
                _mainCanvas.Children.Clear();
                ParsePageFile(xr);
                xr.Close();
            }
        }

        private static void ParsePageFile(XmlReader xr)
        {
            xr.ReadStartElement(PageSilverlightXmlNames.RootElement);

            xr.ReadStartElement(PageSilverlightXmlNames.PageNumberElement);
            int pageNumber = xr.ReadContentAsInt();
            xr.ReadEndElement();

            xr.ReadStartElement(PageSilverlightXmlNames.PageImageElement);
            String imageFileLocation = xr.ReadContentAsString();
            xr.ReadEndElement();

            CurrentPage = new ActiveStoryPage(pageNumber, imageFileLocation);
            Image tempImage = new Image();
            tempImage.Source = new BitmapImage(new Uri(GetAbsoluteDataPath(imageFileLocation)));
            _mainCanvas.Width = tempImage.Width;
            _mainCanvas.Height = tempImage.Height;
            _mainCanvas.Children.Add(tempImage);

            xr.ReadStartElement(PageSilverlightXmlNames.ButtonDetailCollectionElement);

            xr.ReadStartElement(PageSilverlightXmlNames.ButtonCountElement);
            int buttonCount = xr.ReadContentAsInt();
            xr.ReadEndElement();

            for (int i = 0; i < buttonCount; i++)
            {
                xr.ReadStartElement(PageSilverlightXmlNames.ButtonDetailElement);

                xr.ReadStartElement(PageSilverlightXmlNames.ButtonTopElement);
                double top = xr.ReadContentAsDouble();
                xr.ReadEndElement();

                xr.ReadStartElement(PageSilverlightXmlNames.ButtonLeftElement);
                double left = xr.ReadContentAsDouble();
                xr.ReadEndElement();

                xr.ReadStartElement(PageSilverlightXmlNames.ButtonWidthElement);
                double width = xr.ReadContentAsDouble();
                xr.ReadEndElement();

                xr.ReadStartElement(PageSilverlightXmlNames.ButtonHeightElement);
                double height = xr.ReadContentAsDouble();
                xr.ReadEndElement();

                xr.ReadStartElement(PageSilverlightXmlNames.ButtonContentElement);
                int content = xr.ReadContentAsInt();
                xr.ReadEndElement();

                xr.ReadStartElement(PageSilverlightXmlNames.ButtonTargetPageElement);
                int targetPage = xr.ReadContentAsInt();
                if (!PageLookupTable.ContainsKey(targetPage))
                    targetPage = -1;
                xr.ReadEndElement();

                xr.ReadEndElement();    //End Current Button
            
                Button tempButton = new Button();
                tempButton.Style = (Style)Application.Current.Resources["TransButton"];
                tempButton.Background = new SolidColorBrush(Colors.Transparent);
                tempButton.BorderBrush = new SolidColorBrush(Colors.Gray);
                tempButton.BorderThickness = new Thickness(0.1f);
                tempButton.Foreground = new SolidColorBrush(Colors.Transparent);
                tempButton.Content = content;
                tempButton.Name = "Page" + pageNumber + "Button" + content;
                tempButton.Width = width;
                tempButton.Height = height;
                tempButton.Click+=new RoutedEventHandler(delegate(object sender2, RoutedEventArgs e2)
                    {
                        if (!CurrentPage.ButtonMap.ContainsKey(content))
                        {
                            return;
                        }
                        PageTransitioning(content);
                        _mainCanvas.Children.Remove(tempButton);
                        LoadPage(CurrentPage.ButtonMap[content]);
                    });
                _mainCanvas.Children.Add(tempButton);
                Canvas.SetLeft(tempButton, left);
                Canvas.SetTop(tempButton, top);

                if (!CurrentPage.ButtonMap.ContainsKey(content))
                {
                    CurrentPage.ButtonMap.Add(content, targetPage);
                }
            }
            //xr.ReadEndElement();   //End Button Collection Element

            //xr.ReadEndElement();   //End Root Element
        }


        static void uploadDataWebClient_OpenWriteCompleted(object sender, OpenWriteCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                throw new Exception("Could not open stream.");
                // TODO: (Add code to display error or downgrade gracefully.)
            }
            else
            {
                Stream stream = e.Result;
                XmlWriterSettings xws = new XmlWriterSettings();
                xws.Indent = true;
                xws.CloseOutput = true;
                XmlWriter xw = XmlWriter.Create(stream, xws);
                xw.WriteStartDocument();
                xw.WriteStartElement(SilverlightCollectedDataXmlElements.RootElement);

                xw.WriteStartElement(SilverlightCollectedDataXmlElements.PageCountElement);
                xw.WriteValue(userPageSequencedData.Count);
                xw.WriteEndElement();

                foreach (CollectedDataItem item in userPageSequencedData)
                {
                    xw.WriteStartElement(SilverlightCollectedDataXmlElements.PageItemElement);

                    xw.WriteStartElement(SilverlightCollectedDataXmlElements.PageNumberElement);
                    xw.WriteValue(item.PageNumber);
                    xw.WriteEndElement();

                    xw.WriteStartElement(SilverlightCollectedDataXmlElements.DurationElement);
                    xw.WriteValue(item.PageDuration.ToString());
                    xw.WriteEndElement();

                    xw.WriteStartElement(SilverlightCollectedDataXmlElements.CommentsElement);
                    xw.WriteValue(System.Windows.Browser.HttpUtility.HtmlEncode(item.Comments));
                    xw.WriteEndElement();

                    xw.WriteStartElement(SilverlightCollectedDataXmlElements.SourceButtonNumElement);
                    xw.WriteValue(item.ButtonNumberClicked);
                    xw.WriteEndElement();


                    xw.WriteStartElement(SilverlightCollectedDataXmlElements.MousePointListElement);

                    xw.WriteStartElement(SilverlightCollectedDataXmlElements.MousePointCount);
                    xw.WriteValue(item.MousePointList.Count);
                    xw.WriteEndElement();

                    foreach (Point pointItem in item.MousePointList)
                    {
                        xw.WriteStartElement(SilverlightCollectedDataXmlElements.MousePointElement);

                        xw.WriteStartElement(SilverlightCollectedDataXmlElements.MousePointXElement);
                        xw.WriteValue(pointItem.X);
                        xw.WriteEndElement();

                        xw.WriteStartElement(SilverlightCollectedDataXmlElements.MousePointYElement);
                        xw.WriteValue(pointItem.Y);
                        xw.WriteEndElement();

                        xw.WriteEndElement();
                    }
                    xw.WriteEndElement();   //End Moue Point List Element

                    xw.WriteEndElement();   //End Page Item Element
                }

                

                xw.WriteEndElement();   //End Root Element
                xw.WriteEndDocument();
                xw.Flush();
                xw.Close();
                System.Windows.Browser.HtmlPage.Window.Navigate(new Uri(GetAbsoluteServerPath("ThankYou.aspx")));
            }
        }

        /// <summary>
        /// Gets web address of WoZData/filename on server
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        public static string GetAbsoluteDataPath(string filename)
        {
            string uri = Application.Current.Host.Source.AbsoluteUri;
            int index = uri.IndexOf(CLIENTBINFOLDER);
            uri = uri.Substring(0, index) + "/WoZData/" + filename;
            return uri;
        }

        /// <summary>
        /// Gets web address of server
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        public static string GetAbsoluteServerPath(string filename)
        {
            string uri = Application.Current.Host.Source.AbsoluteUri;
            int index = uri.IndexOf(CLIENTBINFOLDER);
            uri = uri.Substring(0, index) + "/" + filename;
            return uri;
        }
    }
    public static class ProjectSilverlightXmlNames
    {
        public const string RootElement = "Project";
        public const string ProjectNameElement = "ProjectName";
        public const string StartupPageElement = "StartupPage";
        public const string PageCollectionElement = "PageCollection";
        public const string PageCountElement = "PageCount";
        public const string PageFileElement = "PageFile";
        public const string PageNumberElement = "PageNumber";
        public const string PageFileLocationElement = "PageLocation";
    }

    public static class PageSilverlightXmlNames
    {
        public const string RootElement = "Page";
        public const string PageNumberElement = "PageNumber";
        public const string PageImageElement = "PageImage";
        public const string ButtonDetailCollectionElement = "ButtonDetails";
        public const string ButtonCountElement = "ButtonCount";
        public const string ButtonDetailElement = "ButtonDetail";
        public const string ButtonTopElement = "Top";
        public const string ButtonLeftElement = "Left";
        public const string ButtonWidthElement = "Width";
        public const string ButtonHeightElement = "Height";
        public const string ButtonContentElement = "Content";
        public const string ButtonTargetPageElement = "TargetPage";
    }
    
    public static class SilverlightSettingsXmlNames
    {
        public const string RootElement = "SilverlightSettings";
        public const string ProjectFileLocationElement = "ProjectFileLocation";
        public const string TaskInstructionsElement = "TaskInstructions";
    }

    public static class SilverlightCollectedDataXmlElements
    {
        public const string RootElement = "CollectedData";
        public const string PageCountElement = "PageCount";
        public const string PageItemElement = "PageInstance";
        public const string PageNumberElement = "PageNumber";
        public const string DurationElement = "Duration";
        public const string CommentsElement = "Comments";
        public const string SourceButtonNumElement = "SourceButtonNumber";
        public const string MousePointListElement = "MousePointList";
        public const string MousePointCount = "MousePointCount";
        public const string MousePointElement = "MousePoint";
        public const string MousePointXElement = "X";
        public const string MousePointYElement = "Y";
    }
}
